﻿using System.Security.Cryptography;
using System.Text;

namespace AdminBase.Common.Helpers
{
    /// <summary>
    /// 密码帮助类
    /// </summary>
    public class PasswordHelper
    {
        /// <summary>
        /// 生成随机盐值
        /// </summary>
        /// <returns>Base64字符</returns>
        public static string GenerateSalt()
        {
            const int saltSize = 32; // 可以根据需要调整盐值的大小
            byte[] salt = new byte[saltSize];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(salt);
            }
            return Convert.ToBase64String(salt);
        }

        /// <summary>
        /// 生成哈希密码
        /// </summary>
        /// <param name="password">密码</param>
        /// <param name="salt">盐值</param>
        /// <returns></returns>
        public static string HashPassword(string password, string salt)
        {
            byte[] saltBytes = Convert.FromBase64String(salt);
            byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
            byte[] allBytes = new byte[saltBytes.Length + passwordBytes.Length];
            Buffer.BlockCopy(saltBytes, 0, allBytes, 0, saltBytes.Length);
            Buffer.BlockCopy(passwordBytes, 0, allBytes, saltBytes.Length, passwordBytes.Length);

            using (var sha256 = SHA256.Create())
            {
                byte[] hashBytes = sha256.ComputeHash(allBytes);
                return Convert.ToBase64String(hashBytes);
            }
        }

        /// <summary>
        /// 恒定时间比较
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool ConstantTimeEquals(byte[] a, byte[] b)
        {
            if (a == null || b == null || a.Length != b.Length)
            {
                return false;
            }

            int result = 0;
            for (int i = 0; i < a.Length; i++)
            {
                // 位或赋值运算符：遇到结果得1才会赋值给result
                result |= a[i] - b[i];
            }

            // 将结果转换为布尔值，确保整个过程是恒定时间的
            // 如果result为0，则所有字节都相等，返回true；否则返回false
            return result == 0;
        }

        /// <summary>
        /// 验证密码
        /// </summary>
        /// <param name="password">密码</param>
        /// <param name="storedHash">哈希密码</param>
        /// <param name="salt">盐值</param>
        /// <returns></returns>
        public static bool VerifyPassword(string password, string storedHash, string salt)
        {
            string hashOfInput = string.Empty;

            if (string.IsNullOrEmpty(salt))
            {
                // 将字符串转换为字节数组
                byte[] passwordByteArray = Encoding.UTF8.GetBytes(password);
                // 将字节数组转换为 Base64 编码的字符串
                hashOfInput = Convert.ToBase64String(passwordByteArray);

                storedHash = Convert.ToBase64String(Encoding.UTF8.GetBytes(storedHash));
            }
            else
            {
                // 输入的密码转成哈希密码
                hashOfInput = HashPassword(password, salt);
            }
            // 将字符串哈希转换为字节数组以便进行恒定时间比较
            byte[] hashOfInputBytes = Convert.FromBase64String(hashOfInput);
            byte[] storedHashBytes = Convert.FromBase64String(storedHash);
            // 使用恒定时间比较函数比较哈希值
            return ConstantTimeEquals(hashOfInputBytes, storedHashBytes);
        }
    }
}
